package com.bugsnag.ohos;

import java.io.IOException;
import java.util.Map;
import java.util.function.BiConsumer;

/**
 * Represents a single stackframe from a [Throwable]
 *
 * @since 2021-07-01
 */
public class Stackframe implements JsonStream.Streamable {
    /**
     * The name of the method that was being executed
     */
    private String method;
    /**
     * The location of the source file
     */
    private String file;
    /**
     * The line number within the source file this stackframe refers to
     */
    private Number lineNumber;
    /**
     * Whether the package is considered to be in your project for the purposes of grouping and
     * readability on the Bugsnag dashboard. Project package names can be set in
     * [Configuration.projectPackages]
     */
    private boolean inProject;
    /**
     * Lines of the code surrounding the frame, where the lineNumber is the key (React Native only)
     */
    private Map<String, String> code;

    /**
     * The column number of the frame (React Native only)
     */
    private Number columnNumber;

    /**
     * The type of the error
     */
    private ErrorType type = null;
    private NativeStackframe nativeFrame;

    /**
     * constructor
     *
     * @param nativeFrame
     */
    public Stackframe(NativeStackframe nativeFrame) {
        this(nativeFrame.method, nativeFrame.file, nativeFrame.lineNumber, false, null, null);
        this.nativeFrame = nativeFrame;
        this.type = nativeFrame.type;
    }

    /**
     * constructor
     *
     * @param method
     * @param file
     * @param lineNumber
     * @param inProject
     * @param code
     * @param columnNumber
     */
    public Stackframe(String method, String file, Number lineNumber,
                      boolean inProject, Map<String, String> code, Number columnNumber) {
        this.method = method;
        this.file = file;
        this.lineNumber = lineNumber;
        this.inProject = inProject;
        this.code = code;
        this.columnNumber = columnNumber;
    }

    public String getMethod() {
        return method;
    }

    /**
     * set method
     *
     * @param method
     */
    public void setMethod(String method) {
        nativeFrame.method = method;
        this.method = method;
    }

    public String getFile() {
        return file;
    }

    /**
     * set file
     *
     * @param file
     */
    public void setFile(String file) {
        nativeFrame.file = file;
        this.file = file;
    }

    public Number getLineNumber() {
        return lineNumber;
    }

    /**
     * set line number
     *
     * @param lineNumber
     */
    public void setLineNumber(Number lineNumber) {
        nativeFrame.lineNumber = lineNumber;
        this.lineNumber = lineNumber;
    }

    public boolean getInProject() {
        return inProject;
    }

    public void setInProject(Boolean inProject) {
        this.inProject = inProject;
    }

    public ErrorType getType() {
        return type;
    }

    /**
     * set type
     *
     * @param type
     */
    public void setType(ErrorType type) {
        nativeFrame.type = type;
        this.type = type;
    }

    @Override
    public void toStream(JsonStream writer) throws IOException {
        NativeStackframe ndkFrame = nativeFrame;
        if (ndkFrame != null) {
            ndkFrame.toStream(writer);
            return;
        }

        writer.beginObject();
        writer.name("method").value(method);
        writer.name("file").value(file);
        writer.name("lineNumber").value(lineNumber);
        writer.name("inProject").value(inProject);
        writer.name("columnNumber").value(columnNumber);

        if (type != null) {
            writer.name("type").value(type.desc);
        }

        if (code != null) {
            writer.name("code");
            code.forEach(new BiConsumer<String, String>() {
                @Override
                public void accept(String key, String value) {
                    try {
                        writer.beginObject();
                        writer.name(key);
                        writer.value(value);
                        writer.endObject();
                    } catch (IOException e) {
                        e.getMessage();
                    }
                }
            });
        }
        writer.endObject();
    }
}
